from ..attributes import *


class Alignment(Object):
    alignee = Attribute()
    aligner = Attribute(initiable = False)
    
    x_value = Attribute()
    y_value = Attribute()
    
    x_displacement = Attribute(initial = 0, writable = True)
    y_displacement = Attribute(initial = 0, writable = True)
    
    def displacement(self, dx, dy = None):
        if dy is None:
            dy = dx
        
        self.x_displacement = dx
        self.y_displacement = dy
        
        return self
    
    def attach(self, aligner):
        self.aligner = aligner
        self.alignee.add_aligner(self.aligner)
        self.fire()
    
    def detach(self):
        self.alignee.drop_aligner(self.aligner)
        
        self.alignee = None
    
    def align(self, position, length, distance, displacement):
        num = getattr(self.alignee, position)
        
        if distance != 0:
            num += getattr(self.alignee, length) * distance
            num -= getattr(self.aligner, length) * distance
        
        if displacement != 0:
            num += getattr(self.aligner.screen, length + '_from_pixels')(displacement)
        
        setattr(self.aligner, position, num)
    
    def fire(self):
        if not self.aligner.is_ready:
            return
        
        if self.x_value is not None:
            self.align('left', 'raw_width', self.x_value, self.x_displacement)
        
        if self.y_value is not None:
            self.align('top', 'raw_height', self.y_value, self.y_displacement)
        
        self.aligner.aligned()


class AlignedObject(Object):
    alignment = Attribute(initiable = False)
    aligners  = SetAttribute(initiable = False)
    
    top  = Attribute(initiable = False, writable = True)
    left = Attribute(initiable = False, writable = True)
    
    @property
    def is_ready(self):
        return True
    
    @property
    def raw_width(self):
        return self.width
    
    @property
    def raw_height(self):
        return self.height
    
    @property
    def bottom(self):
        return self.top + self.raw_height
    
    @property
    def right(self):
        return self.left + self.raw_width
    
    @bottom.setter
    def bottom(self, num):
        self.top = num - self.raw_height
    
    @right.setter
    def right(self, num):
        self.left = num - self.raw_width
    
    def align_at(self, alignment):
        if self.alignment is not None:
            self.alignment.detach()
        
        self.alignment = alignment
        self.alignment.attach(self)
    
    def add_aligner(self, aligner):
        self._aligners.add(aligner)
    
    def drop_aligner(self, aligner):
        self._aligners.remove(aligner)
    
    def update_alignments(self):
        for aligner in self.aligners:
            aligner.alignment.fire()
    
    @property
    def alignment_point(self):
        return AlignmentFactory(self)
    
    def aligned(self):
        pass


class AlignmentFactory(Object):
    alignee = Attribute()
    
    @property
    def top(self):
        return Alignment(self.alignee, 0.5, 0)
    
    @property
    def bottom(self):
        return Alignment(self.alignee, 0.5, 1)
    
    @property
    def left(self):
        return Alignment(self.alignee, 0, 0.5)
    
    @property
    def right(self):
        return Alignment(self.alignee, 1, 0.5)
    
    @property
    def top_left(self):
        return Alignment(self.alignee, 0, 0)
    
    @property
    def bottom_left(self):
        return Alignment(self.alignee, 0, 1)
    
    @property
    def top_right(self):
        return Alignment(self.alignee, 1, 0)
    
    @property
    def bottom_right(self):
        return Alignment(self.alignee, 1, 1)
    
    @property
    def center(self):
        return Alignment(self.alignee, 0.5, 0.5)

